package com.piece.core.framework.util.security.coder;

import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * B编码编码器
 */
@Slf4j
public class BEncodeEncoder {

    /**
     * >List
     */
    private List<Object> list;
    /**
     * Map
     */
    private Map<String, Object> map;
    /**
     * 数据类型
     */
    private BEncodeDecoder.Type type;
    /**
     * 输出数据
     */
    private final ByteArrayOutputStream outputStream;

    private BEncodeEncoder() {
        this.outputStream = new ByteArrayOutputStream();
    }

    public static final BEncodeEncoder newInstance() {
        return new BEncodeEncoder();
    }

    /**
     * 新建List
     */
    public BEncodeEncoder newList() {
        this.type = BEncodeDecoder.Type.LIST;
        this.list = new ArrayList<>();
        return this;
    }

    /**
     * 新建Map
     */
    public BEncodeEncoder newMap() {
        this.type = BEncodeDecoder.Type.MAP;
        this.map = new LinkedHashMap<>();
        return this;
    }

    /**
     * >向List中添加数据
     */
    public BEncodeEncoder put(Object value) {
        if (BEncodeDecoder.Type.LIST == this.type) {
            this.list.add(value);
        }
        return this;
    }

    /**
     * 向List中添加数据
     */
    public BEncodeEncoder put(List<?> list) {
        if (BEncodeDecoder.Type.LIST == this.type) {
            this.list.addAll(list);
        }
        return this;
    }

    /**
     * 向Map中添加数据
     */
    public BEncodeEncoder put(String key, Object value) {
        if (BEncodeDecoder.Type.MAP == this.type) {
            this.map.put(key, value);
        }
        return this;
    }

    /**
     * 向Map中添加数据
     */
    public BEncodeEncoder put(Map<String, Object> map) {
        if (BEncodeDecoder.Type.MAP == this.type) {
            this.map.putAll(map);
        }
        return this;
    }

    /**
     * 将List和Map中的数据写入字符流
     */
    public BEncodeEncoder flush() {
        if (BEncodeDecoder.Type.MAP == this.type) {
            this.writeMap(this.map);
        } else if (BEncodeDecoder.Type.LIST == this.type) {
            this.writeList(this.list);
        }
        return this;
    }

    /**
     * 写入字节数组
     */
    public BEncodeEncoder write(byte[] bytes) {
        try {
            if (null != bytes) {
                this.outputStream.write(bytes);
            }
        } catch (IOException e) {
            log.error("B编码输出异常", e);
        }
        return this;
    }

    /**
     * 写入字符
     */
    private void write(char value) {
        this.outputStream.write(value);
    }

    /**
     * 写入B编码List
     */
    public BEncodeEncoder writeList(List<?> list) {
        if (null == list) {
            return this;
        }
        this.write(BEncodeDecoder.TYPE_L);
        list.forEach(value -> {
            this.writeValue(value);
        });
        this.write(BEncodeDecoder.TYPE_E);
        return this;
    }

    /**
     * 写入B编码Map
     */
    public BEncodeEncoder writeMap(Map<?, ?> map) {
        if (null == map) {
            return this;
        }
        this.write(BEncodeDecoder.TYPE_D);
        map.forEach((key, value) -> {
            final String keyValue = key.toString();
            final byte[] keyValues = keyValue.getBytes();
            this.writeBytes(keyValues);
            this.writeValue(value);
        });
        this.write(BEncodeDecoder.TYPE_E);
        return this;
    }

    /**
     * 写入B编码数据
     */
    private void writeValue(Object value) {
        if (value instanceof Number) {
            this.writeNumber((Number) value);
        } else if (value instanceof byte[]) {
            this.writeBytes((byte[]) value);
        } else if (value instanceof String) {
            this.writeBytes(((String) value).getBytes());
        } else if (value instanceof List) {
            writeList((List<?>) value);
        } else if (value instanceof Map) {
            writeMap((Map<?, ?>) value);
        } else {
            // 类型不支持时添加空字符数组
            this.writeBytes(new byte[]{});
        }
    }

    /**
     * 写入B编码数值
     */
    private void writeNumber(Number number) {
        this.write(BEncodeDecoder.TYPE_I);
        this.write(number.toString().getBytes());
        this.write(BEncodeDecoder.TYPE_E);
    }

    /**
     * 写入B编码字节数组
     */
    private void writeBytes(byte[] bytes) {
        this.write(String.valueOf(bytes.length).getBytes());
        this.write(BEncodeDecoder.SEPARATOR);
        this.write(bytes);
    }

    /**
     * 获取字符数据
     */
    public byte[] bytes() {
        return this.outputStream.toByteArray();
    }

    /**
     * 将数据转为字符串
     */
    @Override
    public String toString() {
        return new String(bytes());
    }

    /**
     * List转为B编码字节数组
     */
    public static final byte[] encodeList(List<?> list) {
        return newInstance().writeList(list).bytes();
    }

    /**
     * List转为B编码字符串
     */
    public static final String encodeListString(List<?> list) {
        return new String(encodeList(list));
    }

    /**
     * Map转为B编码字节数组
     */
    public static final byte[] encodeMap(Map<?, ?> map) {
        return newInstance().writeMap(map).bytes();
    }

    /**
     * Map转为B编码字符串
     */
    public static final String encodeMapString(Map<?, ?> map) {
        return new String(encodeMap(map));
    }
}
